package com.shiroha.account.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.shiroha.account.pojo.dto.PasswordDTO;
import com.shiroha.common.enums.Gender;
import com.shiroha.common.event.ServiceEvent;
import com.shiroha.common.event.ServiceEventPublisher;
import com.shiroha.common.feign.client.UserClient;
import com.shiroha.common.pojo.dto.PrivacyAttributes;
import com.shiroha.common.enums.PrivacyOptions;
import com.shiroha.common.event.ServiceEventType;
import com.shiroha.common.exception.AuthenticationException;
import com.shiroha.common.exception.BadRequestException;
import com.shiroha.common.exception.InternalServiceException;
import com.shiroha.common.feign.client.NotificationClient;
import com.shiroha.account.config.security.keycloak.KeycloakAdminClient;
import com.shiroha.account.config.security.keycloak.KeycloakErrorCode;
import com.shiroha.account.config.security.keycloak.KeycloakException;
import com.shiroha.common.pojo.dto.UserDTO;
import com.shiroha.account.service.IAccountService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 账户服务
 */
@Service
@Slf4j
@AllArgsConstructor
public class AccountServiceImpl implements IAccountService {

    private final KeycloakAdminClient adminClient;
    private final NotificationClient notificationClient;
    private final UserClient userClient;
    private final ServiceEventPublisher publisher;

    @EventListener(ServiceEvent.class)
    public void onEvent(final ServiceEvent event) {
        switch (event.getEventType()) {
            case USER_CREATED -> handleUserCreated(event);
            case USER_UPDATED -> handleUserUpdated(event);
        }
    }

    // 处理用户创建事件
    private void handleUserCreated(ServiceEvent event) {
        try {
            UserDTO userDTO = event.getDataAsType(UserDTO.class);
            Boolean success = userClient.createUser(userDTO);
            if(!success) {
                log.error("Failed to create user: {}", userDTO);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    // 处理用户更新事件
    private void handleUserUpdated(ServiceEvent event) {
        try {
            UserDTO userDTO = event.getDataAsType(UserDTO.class);
            Boolean success = userClient.updateUser(userDTO);
            if(!success) {
                log.error("Failed to update user: {}", userDTO);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 用户通过用户名 + 密码登录
     *
     * @param username 用户名
     * @param password 密码
     * @return token令牌
     */
    @Override
    public AccessTokenResponse loginByPassword(String username, String password) {
        try {
            return adminClient.fetchToken(username, password);
        } catch (KeycloakException e) {
            log.error("用户{}登录失败", username, e);
            throw new AuthenticationException("用户名或密码错误");
        }
    }

    /**
     * 刷新token
     *
     * @param refreshToken refresh_token
     * @return 刷新后的token
     */
    @Override
    public AccessTokenResponse refreshAccessToken(String refreshToken) {
        try {
            return adminClient.refreshToken(refreshToken);
        } catch (KeycloakException e) {
            log.error("刷新token失败{}", refreshToken, e);
            throw new AuthenticationException("refresh_token过期");
        }
    }

    /**
     * 用户登出
     *
     * @param userId 用户id
     */
    @Override
    public void logout(String userId) {
        try {
            adminClient.logout(userId);
            log.info("user {} logged out", userId);
        }catch (Exception e) {
            log.error("user {} logout error", userId, e);
            throw new AuthenticationException("登出失败");
        }
    }

    /**
     * 创建一个新的用户
     *
     * @param user 要创建的用户对象
     * @return 创建成功的用户对象
     */
    @Override
    public UserDTO createUser(UserDTO user) {
        // 调用通知服务的方法
        boolean isChecked = notificationClient.checkCode(user.getEmail(), user.getCode()).getData();
        // 检验验证码
        if(!isChecked) throw new BadRequestException("邮箱验证码错误");
        log.info("邮箱{}通过验证", user.getEmail());

        UserRepresentation userRepresentation = toUserRepresentation(user);
        try {
            UserRepresentation returnUser = adminClient.createUser(userRepresentation);
            log.info("用户{}创建成功", user.getUsername());
            UserDTO userDTO = toUserDTO(returnUser);
            // 发布事件
            ServiceEvent event = ServiceEvent.create(this, ServiceEventType.USER_CREATED, userDTO);
            publisher.publishEvent(event);
            return userDTO;
        } catch (KeycloakException e) {
            log.error(e.getMessage(), e);
            if(e.getErrorCode().equals(KeycloakErrorCode.ERR_USER_CREATION_INVALID)) {
                throw new BadRequestException(e.getMessage());
            } else {
                throw new InternalServiceException(e.getMessage());
            }
        } catch (JsonProcessingException e) {
            log.error("事件创建失败{}", e.getMessage(), e);
            throw new InternalServiceException(e.getMessage());
        }
    }

    /**
     * 根据ID获取用户
     *
     * @param userId 用户Id
     * @return 找到的用户
     */
    @Override
    @Cacheable(value = "user", key = "#userId")
    public UserDTO getUserById(String userId) {
        return toUserDTO(adminClient.getUserById(userId));
    }

    /**
     * 更新用户信息
     *
     * @param userId 用户ID
     * @param user   DTO对象
     */
    @Override
    public void updateUser(String userId, UserDTO user) {
        UserRepresentation userRepresentation = toUserRepresentation(user);
        try {
            adminClient.updateUser(userRepresentation);
            ServiceEvent event = ServiceEvent.create(this, ServiceEventType.USER_UPDATED, user);
            publisher.publishEvent(event);
        } catch (KeycloakException e) {
            log.error(e.getMessage(), e);
            throw new InternalServiceException(e.getMessage());
        } catch (JsonProcessingException e) {
            log.error("事件创建失败{}", e.getMessage(), e);
            throw new InternalServiceException(e.getMessage());
        }
    }

    /**
     * 更新密码
     *
     * @param userId      用户ID
     * @param passwordDTO DTO对象
     */
    @Override
    public void updatePassword(String userId, PasswordDTO passwordDTO) {
        UserRepresentation userRepresentation = adminClient.getUserById(userId);
        String email = userRepresentation.getEmail();
        boolean isChecked = notificationClient.checkCode(email, passwordDTO.getCode()).getData();
        if(isChecked) throw new BadRequestException("邮箱验证码错误");

        // 获取旧密码
        String oldPassword = userRepresentation.getCredentials().getFirst().getValue();
        if(oldPassword.equals(passwordDTO.getPassword())) {
            throw new BadRequestException("新密码不能与旧密码一致");
        }

        try {
            adminClient.resetPassword(userId, passwordDTO.getPassword());
        } catch (KeycloakException e) {
            log.error(e.getMessage(), e);
            throw new BadRequestException(e.getMessage());
        }
    }

    private UserRepresentation toUserRepresentation(UserDTO user) {
        UserRepresentation userRepresentation = new UserRepresentation();
        userRepresentation.setUsername(user.getUsername());
        userRepresentation.setEmail(user.getEmail());
        userRepresentation.setEmailVerified(true);

        // 添加用户凭证
        CredentialRepresentation credential = new CredentialRepresentation();
        credential.setType(CredentialRepresentation.PASSWORD);
        credential.setValue(user.getPassword());
        credential.setTemporary(false);
        List<CredentialRepresentation> credentials = new ArrayList<>();
        credentials.add(credential);
        userRepresentation.setCredentials(credentials);

        // 使用 Stream 设置用户属性
        Map<String, List<String>> attributes = Stream.of(
                        new AbstractMap.SimpleEntry<>("gender", Optional.ofNullable(user.getGender()).map(Gender::getValue).orElse("O")),
                        new AbstractMap.SimpleEntry<>("avatar", user.getAvatar())
                ).filter(entry -> entry.getValue() != null)
                .collect(Collectors.toMap(Map.Entry::getKey, entry -> List.of(entry.getValue())));

        // 隐私默认设置
        Map<String, List<String>> privacyAttributes = PrivacyAttributes.builder()
                .work(PrivacyOptions.PUBLIC)
                .follow(PrivacyOptions.PUBLIC)
                .like(PrivacyOptions.PUBLIC)
                .favorite(PrivacyOptions.PUBLIC)
                .build().toMap();

        attributes.putAll(privacyAttributes);
        userRepresentation.setAttributes(attributes);

        // 启用账户
        userRepresentation.setEnabled(true);
        return userRepresentation;
    }

    private UserDTO toUserDTO(UserRepresentation user) {
        Map<String, List<String>> attributes = user.getAttributes();
        String avatar = attributes.get("avatar").getFirst();
        Gender gender = Gender.fromValue(attributes.get("gender").getFirst());
        PrivacyAttributes privacyAttributes = PrivacyAttributes.fromMap(user.getAttributes());

        return UserDTO.builder()
                .id(user.getId())
                .username(user.getUsername())
                .avatar(avatar)
                .email(user.getEmail())
                .gender(gender)
                .privacyAttributes(privacyAttributes)
                .build();
    }
}
